﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
using Wintellect.PowerCollections;

namespace Pylor
{
    [Serializable]
    [XmlType(AnonymousType = true, Namespace = Namespace)]
    [XmlRoot(Namespace = Namespace, IsNullable = false)]
    public class Rules : List<Rule>
    {
        public const string Namespace = "http://schemas.mvitorino.com/pylor/2010/03/rules.xsd";

        public bool HasPermission(string role, string resource)
        {
            return HasPermission(new[] {role}, resource);
        }

        public bool HasPermission(IEnumerable<string> roles, string resource)
        {
            var rs = Rule.CreateRoleSet();
            rs.AddMany(roles);
            return HasPermission(rs, resource);
        }

        private bool HasPermission(Set<string> roles, string resource)
        {
            var hasAllow = false;

            foreach (var rule in this)
            {
                var permission = rule.HasPermission(roles, resource);

                if (permission == Permission.Deny)
                    return false;

                if (permission == Permission.Allow)
                    hasAllow = true;
            }

            return hasAllow;
        }

        public static Rules FromString(string s)
        {
            var sr = new StringReader(s);
            return (Rules)new XmlSerializer(typeof(Rules)).Deserialize(sr);
        }

        public override string ToString()
        {
            var s = new StringWriter();
            new XmlSerializer(typeof(Rules)).Serialize(s, this);
            return s.ToString();
        }

        public void Write(TextWriter writer)
        {
            new XmlSerializer(GetType()).Serialize(writer, this);
        }

        public static Rules Load(TextReader input)
        {
            using (var reader = XmlReader.Create(input))
                return Load(reader);
        }

        public static Rules Load(Stream input)
        {
            using (var reader = XmlReader.Create(input))
                return Load(reader);
        }

        public static Rules Load(XmlReader input)
        {
            var settings = new XmlReaderSettings();
            settings.Schemas.Add(Schema());
            settings.ValidationType = ValidationType.Schema;

            // validate the file with the rules schema
            using (var reader = XmlReader.Create(input, settings))
            {
                var rules = (Rules)new XmlSerializer(typeof(Rules)).Deserialize(reader);
                //rules.Validate();
                return rules;
            }
        }

        private const string SchemaResource = "Pylor.Rules.xsd";

        private static XmlSchema Schema()
        {
            var assembly = Assembly.GetExecutingAssembly();
            using (var stream = assembly.GetManifestResourceStream(SchemaResource))
            {
                Debug.Assert(stream != null, string.Format("Assembly resource '{0}' not found.", SchemaResource));
                return XmlSchema.Read(stream, OnValidateSchemaLoad);
            }
        }

        private static void OnValidateSchemaLoad(object sender, ValidationEventArgs args)
        {
            Debug.Fail(string.Format("Embedded resource '{0}' is not a valid xml schema.", SchemaResource));
        }
    }
}